package com.gitee.wsl.mathematics.vector.vec3

import com.gitee.wsl.ext.array.FloatArray
import com.gitee.wsl.io.dataproxy.DataGet
import com.gitee.wsl.io.dataproxy.asProxy
import com.gitee.wsl.mathematics.coordinate.d3.Coordinate3F
import com.gitee.wsl.mathematics.vector.vec2.Vec2f
import com.gitee.wsl.mathematics.vector.vec4.Vec4f


open class Vec3f(override val proxy: DataGet<Float>):
    Coordinate3F<Vec3f>{
    constructor(x: Float=0.0f,
                y: Float=0.0f,
                z: Float=0.0f):this(FloatArray(x,y,z).asProxy())
    constructor() : this(0.0f, 0.0f, 0.0f)
    constructor(a: Number) : this(a.toDouble(), a.toDouble(), a.toDouble())
    constructor(x: Number, y: Number, z: Number) : this(x.toFloat(), y.toFloat(), z.toFloat())
    constructor(base: Vector3<*,*>) : this(base.x, base.y, base.z)

    override val self
        get() = this

    //override val xy get() = Vec2f(x,y)
    //override val xz get() = Vec2f(x,z)
    //override val yz get() = Vec2f(y,z)
//
    //override val vec2 get() = Vec2f(x, y)
    //override val vec3 get() = this
    override val vec4 get() = Vec4f(x, y, z, 0.0)

    override fun createVec2(a: Float, b: Float): Vec2f = Vec2f(a,b)

    override fun create(x: Float, y: Float, z: Float): Vec3f = Vec3f(x, y, z)

    companion object {
        val ONE  = Vec3f(1.0, 1.0, 1.0)
        val ZERO = Vec3f(0.0, 0.0, 0.0)
        val right get() = Vec3f(1.0, 0.0, 0.0)
        val left get() = Vec3f(-1.0, 0.0, 0.0)
        val up get() = Vec3f(0.0, 1.0, 0.0)
        val down get() = Vec3f(0.0, -1.0, 0.0)
        val forward get() = Vec3f(0.0, 0.0, 1.0)
        val backward get() = Vec3f(0.0, 0.0, -1.0)
        val negativeInfinity get() = Vec3f(Float.NEGATIVE_INFINITY)
        val positiveInfinity get() = Vec3f(Float.POSITIVE_INFINITY)

        val UNIT_X = Vec3f(1f, 0f, 0f)
        val UNIT_Y = Vec3f(0f, 1f, 0f)
        val UNIT_Z = Vec3f(0f, 0f, 1f)

        val X_AXIS = Vec3f(1f, 0f, 0f)
        val Y_AXIS = Vec3f(0f, 1f, 0f)
        val Z_AXIS = Vec3f(0f, 0f, 1f)

        val NEG_X_AXIS = Vec3f(-1f, 0f, 0f)
        val NEG_Y_AXIS = Vec3f(0f, -1f, 0f)
        val NEG_Z_AXIS = Vec3f(0f, 0f, -1f)

        val UNIT_XYZ = ONE.normalized

    }

    override fun toString(): String {
        val xInt = x.toInt(); val yInt = y.toInt(); val zInt = z.toInt()
        val xs = if (x == xInt.toFloat()) xInt.toString() else x.toString()
        val ys = if (y == yInt.toFloat()) yInt.toString() else y.toString()
        val zs = if (z == zInt.toFloat()) zInt.toString() else z.toString()

        return "[$xs, $ys, $zs]"
    }

    override fun equals(other: Any?) = other is Vec3f && x == other.x && y == other.y && z == other.z
    override fun hashCode() = 31 * (31 * x.hashCode() + y.hashCode()) + z.hashCode()

}

/*open class Vec3f2(x: Float, y: Float, z: Float, override val values: FloatArray = FloatArray(x,y,z)):
    Vector3f<Vec3f>, Coordinate3F<Vec3f> {
    constructor() : this(0.0f, 0.0f, 0.0f)
    constructor(a: Number) : this(a.toDouble(), a.toDouble(), a.toDouble())
    constructor(x: Number, y: Number, z: Number) : this(x.toFloat(), y.toFloat(), z.toFloat())

    *//*open val x
        get() = this[0]

    open val y
        get() = this[1]

    open val z
        get() = this[2]

    override val length
        get() = sqrt(sqrLength)
        
    override val sqrLength get() = x * x + y * y + z * z

    override val normalized get() = this / length

    override val abs get() = Vec3f(abs(x), abs(y), abs(z))
    override val sign get() = Vec3f(sign(x), sign(y), sign(z))
    override val ceil get() = Vec3f(ceil(x), ceil(y), ceil(z))
    override val floor get() = Vec3f(floor(x), floor(y), floor(z))
    override val round get() = Vec3f(round(x), round(y), round(z))*//*
    override val vec2 get() = Vec2f(x, y)
    override val vec3 get() = this
    override val vec4 get() = Vec4f(x, y, z, 0.0)

    companion object {
        val one get() = Vec3f(1.0, 1.0, 1.0)
        val zero get() = Vec3f(0.0, 0.0, 0.0)
        val right get() = Vec3f(1.0, 0.0, 0.0)
        val left get() = Vec3f(-1.0, 0.0, 0.0)
        val up get() = Vec3f(0.0, 1.0, 0.0)
        val down get() = Vec3f(0.0, -1.0, 0.0)
        val forward get() = Vec3f(0.0, 0.0, 1.0)
        val backward get() = Vec3f(0.0, 0.0, -1.0)
        val negativeInfinity get() = Vec3f(Float.NEGATIVE_INFINITY)
        val positiveInfinity get() = Vec3f(Float.POSITIVE_INFINITY)

        val UNIT_X = Vec3f(1f, 0f, 0f)
        val UNIT_Y = Vec3f(0f, 1f, 0f)
        val UNIT_Z = Vec3f(0f, 0f, 1f)
    }

    open operator fun plus(other: Vec2f) = Vec3f(x + other.x, y + other.y, z)
    override operator fun plus(other: Vec3f) = Vec3f(x + other.x, y + other.y, z + other.z)
    open operator fun plus(other: Vec4f) = Vec4f(x + other.x, y + other.y, z + other.z, other.w)

    open operator fun minus(other: Vec2f) = Vec3f(x - other.x, y - other.y, z)
    override operator fun minus(other: Vec3f) = Vec3f(x - other.x, y - other.y, z - other.z)
    open operator fun minus(other: Vec4f) = Vec4f(x - other.x, y - other.y, z - other.z, -other.w)

    override operator fun times(a: Number) = Vec3f(x * a.toDouble(), y * a.toDouble(), z * a.toDouble())
    open operator fun times(other: Vec2f) = Vec3f(x * other.x, y * other.y, 0.0)
    override operator fun times(other: Vec3f) = Vec3f(x * other.x, y * other.y, z * other.z)
    open operator fun times(other: Vec4f) = Vec4f(x * other.x, y * other.y, z * other.z, 0.0)
    operator fun times(rotation: Quaternion) = rotation * this

    override operator fun div(a: Number) = Vec3f(x / a.toDouble(), y / a.toDouble(), z / a.toDouble())
    open operator fun div(other: Vec2f) = Vec3f(x / other.x, y / other.y, Float.POSITIVE_INFINITY)
    override operator fun div(other: Vec3f) = Vec3f(x / other.x, y / other.y, z / other.z)
    open operator fun div(other: Vec4f) = Vec4f(x / other.x, y / other.y, z / other.z, Float.POSITIVE_INFINITY)
    override fun divBy(a: Number): Vec3f = Vec3f(a.toDouble() / x, a.toDouble() / y, a.toDouble() / z)


    override operator fun rem(a: Number) = Vec3f(x % a.toDouble(), y % a.toDouble(), z % a.toDouble())
    open operator fun rem(other: Vec2f) = Vec3f(x % other.x, y % other.y, Float.NaN)
    override operator fun rem(other: Vec3f) = Vec3f(x % other.x, y % other.y, z % other.z)
    open operator fun rem(other: Vec4f) = Vec4f(x % other.x, y % other.y, z % other.z, Float.NaN)
    override fun remBy(a: Number): Vec3f = Vec3f(a.toDouble() % x, a.toDouble() % y, a.toDouble() % z)


    //operator fun compareTo(other: Vec2f) = length.compareTo(other.length)
    //override operator fun compareTo(other: Vec3f) = length.compareTo(other.length)
    //operator fun compareTo(other: Vec4f) = length.compareTo(other.length)

    //override operator fun unaryMinus() = Vec3f(-x, -y, -z)


    *//**The distance from this vector to [other].*//*
    override infix fun distanceTo(other: Vec3f) = (other - this).length
    *//**The distance from this vector to [other].*//*
    infix fun distanceTo(other: Vec2f) = (other - this).length
    *//**The distance from this vector to [other].*//*
    infix fun distanceTo(other: Vec4f) = (other - this).length

    *//**The normal direction from this vector to [other].*//*
    infix fun directionTo(other: Vec2f) = (other - this).normalized
    *//**The normal direction from this vector to [other].*//*
    override infix fun directionTo(other: Vec3f) = (other - this).normalized
    *//**The normal direction from this vector to [other].*//*
    infix fun directionTo(other: Vec4f) = (other - this).normalized

    *//**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*
    override infix fun dot(other: Vector2<Float, *>) = x * other.x + y * other.y
    *//**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*
    //infix fun dot(other: Vector3<Float, *>) = x * other.x + y * other.y + z * other.z
    *//**
     * Dot product.
     * @return The dot product between this vector and [other].
     *//*
     //infix fun dot(other: Vector4<Float, *>) = x * other.x + y * other.y + z * other.z

    *//**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*
    //override infix fun angleTo(other: Vec2f) = atan2(cross(other).length, dot(other))
    *//**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*
    //override infix fun angleTo(other: Vec3f) = atan2(cross(other).length, dot(other))

    override fun angleTo(other: Vector2<Float, *>): Float = atan2(cross(other).length, dot(other))

    override fun angleTo(other: Vector3<Float, *>): Float = atan2(cross(other).length, dot(other))

    *//**
     * The angle between two vectors.
     * @return The angle between this vector and [other].
     *//*
//    infix fun angleTo(other: Vec4) = other angleTo this

     fun signedAngleTo(other: Vector3<Float, *>, axis: Vector3<Float, *>): Float {
        val angle = angleTo(other)
        val cross = cross(other)
        val sign = sign(axis dot cross)
        return angle * sign
    }

    fun signedAngleTo(other: Vec2f, axis: Vec3f) = signedAngleTo(other.vec3, axis)

    *//**
     * Cross product.
     * @return The cross product between this vector and [other].
     *//*
    open infix fun cross(other: Vector2<Float, *>) = Vec3f(-z * other.y, z * other.x, x * other.y - y * other.x)

    *//**
     * Cross product.
     * @return The cross product between this vector and [other].
     *//*
    override infix fun cross(other: Vector3<Float, *>) = Vec3f(
        y * other.z - z * other.y,
        z * other.x - x * other.z,
        x * other.y - y * other.x
    )

    *//**The projection of this vector onto [other].*//*
    override infix fun projectOn(other: Vec2f) = other * dot(other) / other.sqrLength
    *//**The projection of this vector onto [other].*//*
    override infix fun projectOn(other: Vec3f) = other * dot(other) / other.sqrLength
    *//**The projection of this vector onto [other].*//*
    override infix fun projectOn(other: Vec4f) = other * dot(other) / other.sqrLength

    infix fun projectOntoPlane(normal: Vec3f): Vec3f {
        val sqrMag = normal dot normal
        if (sqrMag == 0.0f) return this
        val dot = dot(normal)
        return (this - normal) * dot / sqrMag
    }

    *//**Reflects the vector from [normal].*//*
    infix fun reflectFrom(normal: Vec3f): Vec3f {
        val norm = normal.normalized
        val factor = -2.0f * this dot norm
        return factor * norm + this
    }
    *//**Reflects the vector from [normal].*//*
    infix fun reflectFrom(normal: Vec2f) = reflectFrom(normal.vec3)
    *//**Reflects the vector from [normal].*//*
    infix fun reflectFrom(normal: Vec4f) = vec4 reflectFrom normal

    *//**
     * Rotates the vector by [angle] radians around [axis].
     * @param angle The desired angle in **radians**.
     * @param axis The desired **normalized** vector to rotate this vector around.
     * @return The rotated vector
     *//*
    fun rotatedBy(angle: Number, axis: Vec3f) = Quaternion(angle, axis) * this


    override fun toString(): String {
        val xInt = x.toInt(); val yInt = y.toInt(); val zInt = z.toInt()
        val xs = if (x == xInt.toFloat()) xInt.toString() else x.toString()
        val ys = if (y == yInt.toFloat()) yInt.toString() else y.toString()
        val zs = if (z == zInt.toFloat()) zInt.toString() else z.toString()

        return "[$xs, $ys, $zs]"
    }

    override fun create(x: Float, y: Float, z: Float): Vec3f = Vec3f(x, y, z)

    override fun equals(other: Any?) = other is Vec3f && x == other.x && y == other.y && z == other.z
    override fun hashCode() = 31 * (31 * x.hashCode() + y.hashCode()) + z.hashCode()
}*/

fun Vec3f.toVec3() = Vec3(x, y, z)